Exceptions which are events that can modify the flow of control through a program.
In Python, exceptions are triggered automatically on errors, and they can be triggered and intercepted by your code.
They are processed by four statements we’ll study in this notebook, the first of which has two variations (listed separately here) and the last of which was an optional extension until Python 2.6 and 3.0:
try/except
:
try/finally
:
raise
:
assert
:
with/as
:
try:
statements # Run this main action first
except name1:
# Run if name1 is raised during try block
statements
except (name2, name3):
# Run if any of these exceptions occur
statements
except name4 as var:
# Run if name4 is raised, assign instance raised to var
statements
except: # Run for all other exceptions raised
statements
else:
statements # Run if no exception was raised during try block
In [1]:
list_of_numbers = [number for number in range(1, 100)]
print(list_of_numbers)
In [10]:
dictionary_of_numbers = {}
for number in list_of_numbers:
dictionary_of_numbers[number**2] = number
try:
index = list_of_numbers.index(2)
value = dictionary_of_numbers[index]
except (ValueError, KeyError):
print('Error Raised, but Controlled! ')
else:
# This executes ONLY if no exception is raised
print('Getting number at position %d : %d' % (index, value))
finally:
# Do cleanup operations
print('Cleaning UP')
The other flavor of the try statement is a specialization that has to do with finalization (a.k.a. termination) actions. If a finally clause is included in a try, Python will always run its block of statements “on the way out” of the try statement, whether an exception occurred while the try block was running or not.
In it's general form, it is:
try:
statements # Run this action first
finally:
statements # Always run this code on the way out
Python 2.6 and 3.0 introduced a new exception-related statement—the with, and its optional as clause. This statement is designed to work with context manager objects, which support a new method-based protocol, similar in spirit to the way that iteration tools work with methods of the iteration protocol.
with open(r'C:\misc\data') as myfile:
for line in myfile:
print(line)
# ...more code here...
... even using multiple context managers:
with open('script1.py') as f1, open('script2.py') as f2:
for (linenum, (line1, line2)) in enumerate(zip(f1, f2)):
if line1 != line2:
print('%s\n%r\n%r' % (linenum, line1, line2))
The expression is evaluated,resulting in an object known as a context manager that must have __enter__
and __exit__
methods
The context manager’s __enter__
method is called. The value it returns is assigned to the variable in the as clause if present, or simply discarded otherwise
The code in the nested with block is executed.
If the with block raises an exception, the __exit__(type,value,traceback)
method is called with the exception details. These are the same three values returned by sys.exc_info
(Python function). If this method returns a false
value, the exception is re-raised; otherwise, the exception is terminated. The exception should normally be reraised so that it is propagated outside the with statement.
If the with block does not raise an exception, the __exit__
method is still called, but its type, value, and traceback arguments are all passed in as None
.
In [4]:
class TraceBlock:
def message(self, arg):
print('running ' + arg)
def __enter__(self):
print('starting with block')
return self
def __exit__(self, exc_type, exc_value, exc_tb):
if exc_type is None:
print('exited normally\n')
else:
print('raise an exception! ' + str(exc_type))
return False # Propagate
In [5]:
with TraceBlock() as action:
action.message('test 1')
print('reached')
In [6]:
with TraceBlock() as action:
action.message('test 2')
raise TypeError()
print('not reached')
In [1]:
class AlreadyGotOne(Exception):
pass
def gail():
raise AlreadyGotOne()
In [2]:
try:
gail()
except AlreadyGotOne:
print('got exception')
In [2]:
class Career(Exception):
def __init__(self, job, *args, **kwargs):
super(Career, self).__init__(*args, **kwargs)
self._job = job
def __str__(self):
return 'So I became a waiter of {}'.format(self._job)
raise Career('Engineer')